package yyl.mvc.plugin.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * 综合Filter，可以Spring代理多个Filter。是过虑器链，处理多个过虑器。在filter.xml中配置<br>
 * @author YYL
 * @version 2014-04-08 1.0
 * @see org.springframework.web.filter.DelegatingFilterProxy
 */
public class CompositeFilter implements Filter {

	private List<? extends Filter> filters = new ArrayList<Filter>();

	/**
	 * 初始化过滤器
	 * @param config 过滤器配置信息
	 */
	public void init(FilterConfig config) throws ServletException {
		for (Filter filter : filters) {
			filter.init(config);
		}
	}

	/**
	 * 运行过滤器
	 * @param request 请求
	 * @param response 应答
	 * @param chain 过滤器链
	 */
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		new VirtualFilterChain(chain, filters).doFilter(request, response);
	}

	/**
	 * 销毁过滤器
	 */
	public void destroy() {
		for (int i = filters.size(); i-- > 0;) {
			Filter filter = filters.get(i);
			filter.destroy();
		}
	}

	/**
	 * 设置代理的过滤器
	 * @param filters 过滤器
	 */
	public void setFilters(List<? extends Filter> filters) {
		this.filters = new ArrayList<Filter>(filters);
	}

	/**
	 * 虚拟过滤器链
	 */
	private static final class VirtualFilterChain implements FilterChain {
		private final FilterChain originalChain;
		private final List<? extends Filter> additionalFilters;
		private int currentPosition = 0;

		private VirtualFilterChain(FilterChain chain, List<? extends Filter> additionalFilters) {
			this.originalChain = chain;
			this.additionalFilters = additionalFilters;
		}

		public void doFilter(final ServletRequest request, final ServletResponse response) throws IOException, ServletException {
			if (currentPosition == additionalFilters.size()) {
				originalChain.doFilter(request, response);
			} else {
				currentPosition++;
				Filter nextFilter = additionalFilters.get(currentPosition - 1);
				nextFilter.doFilter(request, response, this);
			}
		}
	}
}
